import { Form } from 'ant-design-vue'
import type { Rule } from 'ant-design-vue/es/form'
import { reactive, ref, toValue, watch } from 'vue'
import {
  type FloatValidatorMessage,
  type FloatValidatorOptions,
  type NumberValidatorOptions,
  FLOAT_VALIDATOR_RESULT,
  numberValidatorResultIsValid,
  floatValidator,
  numberValidatorAsync
} from '@shared/number'

/**
 * 被编辑字段的边界字段
 */
export interface Section {
  min: number | undefined | null
  max: number | undefined | null
}

enum NullabelType {
  /** null */
  Null = 1 << 1,
  /** undefined */
  Unde = 1 << 2
}

/**
 * 被编辑字段的边界字段对应的列的校验上下文
 */
export interface ValidateContent {
  /** 当前已触发的 */
  validated: Set<number>
  /** 触发联动的索引g */
  lockByIndex: number | null
  /** 空值类型 */
  nullabel: number
  /** 列和 */
  summation: number
  /** 列和校验结果 */
  verifyResult: FLOAT_VALIDATOR_RESULT
  /** 是否已触发过全局校验 */
  isWhole?: boolean
}

/**
 * 被编辑字段的校验上下文
 */
export type ValidateContentState<EditableKey extends string> = {
  [K1 in EditableKey]: {
    [K2 in keyof Section]: ValidateContent
  }
}

/**
 * 验证规则
 */
export type ValidatorOptionMap<EditableKey extends string> = {
  [K in EditableKey]: FloatValidatorOptions
}

/**
 * 验证的错误信息
 */
export type ValidateMessageMap<EditableKey extends string> = {
  [K in EditableKey]: {
    [K1 in keyof Section]: FloatValidatorMessage
  }
}

function toggleSectionType(sectionType: keyof Section) {
  return sectionType === 'min' ? 'max' : 'min'
}

export type EditableRecord<EditableKey extends string> = {
  [K in EditableKey]: Section
}

export function useColumnLinkage<EditableKey extends string, DataRecord extends EditableRecord<EditableKey>>(
  dataSource = ref<DataRecord[]>([]),
  option: {
    editableKeys: EditableKey[]
    validatorOptionMap: ValidatorOptionMap<EditableKey>
    validateMessageMap: ValidateMessageMap<EditableKey>
  }
) {
  /**
   * 全校验模式
   */
  let isWhole = false

  const { editableKeys, validatorOptionMap, validateMessageMap } = option

  const editableSectionKeys: [EditableKey, keyof Section][] = editableKeys
    .map((k1) => {
      return ['min', 'max'].map((k2) => [k1, k2])
    })
    .flat(1) as any

  const validateContentState = reactive(
    editableKeys.reduce((state, key) => {
      state[key] = {
        max: {
          lockByIndex: null,
          validated: new Set(),
          summation: 0,
          nullabel: 0,
          verifyResult: FLOAT_VALIDATOR_RESULT.LESS_THAN_MIN_VALUE
        },
        min: {
          lockByIndex: null,
          validated: new Set(),
          summation: 0,
          nullabel: 0,
          verifyResult: FLOAT_VALIDATOR_RESULT.LESS_THAN_MIN_VALUE
        }
      }

      return state
    }, {} as ValidateContentState<EditableKey>)
  ) as ValidateContentState<EditableKey>

  const validateResult: WeakMap<object, Promise<void>> = new WeakMap()

  const rulesRef = ref<Record<string, Rule[]>>({})

  const { validateInfos, validate, clearValidate } = Form.useForm(dataSource, rulesRef)

  /**
   * 更新验证上下文
   * @param editableKey 被编辑的字段
   * @param sectionType 被编辑字段的边界类型
   */
  function updateValidateContent(editableKey: EditableKey, sectionType: keyof Section) {
    let summation = 0
    let nullabel = 0

    const list = toValue(dataSource) as DataRecord[]

    list.forEach((record) => {
      const value = record[editableKey][sectionType]
      if (value) {
        summation += value
      } else {
        nullabel |= value === null ? NullabelType.Null : NullabelType.Unde
      }
    })

    const validatorOption: FloatValidatorOptions = { ...validatorOptionMap[editableKey] }
    validatorOption.min! *= list.length
    if (sectionType === 'max') delete validatorOption.max

    const ctx = validateContentState[editableKey][sectionType]
    Object.assign(ctx, {
      summation,
      nullabel,
      verifyResult: floatValidator(summation, validatorOption)
    })

    const promise = numberValidatorAsync(summation, validatorOption, validateMessageMap[editableKey][sectionType])
    validateResult.set(ctx, promise)
  }

  const lockSectionLinkage: { [K in EditableKey]?: keyof Section } = {}

  /**
   * 生产表单验证规则
   * @param index 行索引
   * @param editableKey 编辑的字段
   * @param sectionType 被编辑字段的边界类型
   */
  function createRules(index: number, editableKey: EditableKey, sectionType: keyof Section): Rule[] {
    return [
      {
        trigger: ['blur', 'change'],
        validator(_, value: number | undefined | null) {
          console.log({ index, editableKey, sectionType })

          const list = toValue(dataSource) as DataRecord[]

          const validatorOption: NumberValidatorOptions = { ...validatorOptionMap[editableKey] }

          const oppositeSectionType = toggleSectionType(sectionType)
          const oppositeValue = list[index][editableKey][oppositeSectionType]
          if (oppositeValue) {
            // 更新 min 的最大取值 或 更新 max 的最小取值
            validatorOption[oppositeSectionType] = Math[sectionType](
              oppositeValue,
              validatorOption[oppositeSectionType]!
            )
          }

          const promise = numberValidatorAsync(value, validatorOption)

          const ctx = validateContentState[editableKey][sectionType]

          isWhole && (ctx.isWhole = true)

          if (ctx.lockByIndex === null) {
            const lastSummationIsValid = numberValidatorResultIsValid(ctx.verifyResult)

            updateValidateContent(editableKey, sectionType)

            const { nullabel, verifyResult } = ctx

            const summationIsValid = numberValidatorResultIsValid(verifyResult)
            const greaterThanMax = verifyResult === FLOAT_VALIDATOR_RESULT.GREATER_THAN_MAX_VALUE

            // 1. 全量校验
            // 2. 列和无效 && (列和大于最大 || (列和小于最小 && 没有空值))
            if (
              isWhole ||
              (!summationIsValid &&
                (greaterThanMax || (verifyResult === FLOAT_VALIDATOR_RESULT.LESS_THAN_MIN_VALUE && !nullabel)))
            ) {
              ctx.lockByIndex = index

              ctx.validated.clear()
              ctx.validated.add(index)

              // 触发联动
              triggerLinkageValidate(editableKey, sectionType)

              return greaterThanMax ? validateResult.get(ctx) : promise.then(() => validateResult.get(ctx))
            }

            // 重置上一次的联动状态
            !lastSummationIsValid && resetLinkageValidate(editableKey, sectionType)
          } else {
            ctx.validated.add(index)

            promise.finally(() => {
              if (ctx.validated.size === list.length) {
                ctx.lockByIndex = null
              }
            })

            return ctx.verifyResult === FLOAT_VALIDATOR_RESULT.GREATER_THAN_MAX_VALUE
              ? validateResult.get(ctx)
              : promise.then(() => validateResult.get(ctx))
          }

          // 相反的字段有值
          if (oppositeValue) {
            const oppositeCtx = validateContentState[editableKey][oppositeSectionType]

            // 1. 相反列未进行列校验
            // 2. 相反的字段未进行左右校验
            if (oppositeCtx.lockByIndex === null && !lockSectionLinkage[editableKey]) {
              lockSectionLinkage[editableKey] = oppositeSectionType
              validate([`${index}.${editableKey}.${oppositeSectionType}`]).finally(() => {
                delete lockSectionLinkage[editableKey]
              })
            }
          }

          return promise
        }
      }
    ]
  }

  /**
   * 触发联动校验
   */
  function triggerLinkageValidate(editableKey: EditableKey, sectionType: keyof Section) {
    const { lockByIndex } = validateContentState[editableKey][sectionType]

    if (lockByIndex === null) return

    const paths: string[] = []
    const max = dataSource.value.length
    for (let index = 0; index < max; index++) {
      if (index !== lockByIndex) paths.push(`${index}.${editableKey}.${sectionType}`)
    }

    paths.length && validate(paths)
  }

  /**
   * 重置联动校验的状态
   */
  function resetLinkageValidate(editableKey: EditableKey, sectionType: keyof Section) {
    const { lockByIndex, isWhole, nullabel } = validateContentState[editableKey][sectionType]

    const triggerList: string[] = []
    const clearList: string[] = []

    dataSource.value.forEach((record, index) => {
      if (index !== lockByIndex) {
        const list = (record as DataRecord)[editableKey][sectionType] === undefined ? clearList : triggerList
        list.push(`${index}.${editableKey}.${sectionType}`)
      }
    })

    triggerList.length && validate(triggerList)
    !isWhole && clearList.length && clearValidate(clearList)
  }

  /**
   * 全量校验
   */
  function wholeValidate() {
    isWhole = true

    const paths = editableSectionKeys.map(([k1, k2]) => `0.${k1}.${k2}`)
    const promise = validate(paths)
    promise.finally(() => {
      isWhole = false
    })

    return promise
  }

  watch(
    () => dataSource.value.length,
    (currentLength, lastLength) => {
      if (typeof lastLength !== 'number') lastLength = 0

      if (currentLength > lastLength) {
        for (let index = lastLength; index <= currentLength; index++) {
          editableSectionKeys.forEach(([k1, k2]) => {
            rulesRef.value[`${index}.${k1}.${k2}`] = createRules(index, k1, k2)
          })
        }
      }

      if (currentLength < lastLength) {
        for (let index = currentLength; index <= lastLength!; index++) {
          editableSectionKeys.forEach(([k1, k2]) => {
            delete rulesRef.value[`${index}.${k1}.${k2}`]
          })
        }
      }
    },
    { immediate: true }
  )

  return { dataSource, validateContentState, validateInfos, validate, wholeValidate }
}
